using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CSharpExtensions
{

    #region discriminated type
    public sealed class DiscrimatedType<TFirst, TSecond>
    {
        public DiscrimatedType(TFirst data)
        {
            First = data.ToOption();
        }
        public DiscrimatedType(TSecond data)
        {
            Second = data.ToOption();
        }
        private readonly Option<TFirst> First;
        private readonly Option<TSecond> Second;

        public TResult Match<TResult>(
                            Func<TFirst, TResult> first,
                            Func<TSecond, TResult> second)
        {
            return
                First.Select(firsValue =>
                                first(firsValue))
                      .Return(() => second(Second.Value));


        }




        public override int GetHashCode()
        {
            return
                First.Select(
                            firstValue => firstValue.GetHashCode())
                     .Return(() => Second.Value.GetHashCode());
        }
        public override bool Equals(object obj)
        {
            var other = obj as DiscrimatedType<TFirst, TSecond>;
            if (other != null)
            {
                return IsEqual(other);
            }
            return false;
        }
        public bool IsEqual(DiscrimatedType<TFirst, TSecond> other)
        {

            return First.IsEqual(other.First) &&
                   Second.IsEqual(other.Second);
            
                        
        }
        public DiscrimatedType<TFirstResult, TSecondResult> Select<TFirstResult, TSecondResult>(Func<TFirst,TFirstResult> first, Func<TSecond,TSecondResult> second)
        {
            return
                First.Select(firstValue =>
                        new DiscrimatedType<TFirstResult, TSecondResult>(
                                        first( firstValue)))
                      .Return(() => new DiscrimatedType<TFirstResult, TSecondResult>(
                                        second(Second.Value)));
        }


    }
    public static class DiscrimatedType
    {
        public static TCast CommonCast<TFirst, TSecond, TCast>(this DiscrimatedType<TFirst,TSecond> source)
            where TFirst:TCast
            where TSecond: TCast
        {
            return
                source.Match(
                        _ => (TCast)_,
                        _ => (TCast)_);

        }
    }
    

    #endregion

}